package org.sanguine.study.hometasks.second.auxiliary;

import static java.lang.Math.*;

public class Segment implements Comparable<Segment> {
	private final Double x1;
	private final Double x2;
	private final Double y1;
	private final Double y2;
	
	public Segment(Double x1, Double y1, Double x2, Double y2) {
		if (x1 == null || x2 == null || y1 == null || y2 == null) throw new IllegalArgumentException("Coordinates must be non-null");
		
		if (x1 > x2) {
			this.x1 = x2;
			this.y1 = y2;
			this.x2 = x1;
			this.y2 = y1;
		} else {
			this.x1 = x1;
			this.x2 = x2;
			if (x1.equals(x2)) {
				this.y1 = min(y1, y2);
				this.y2 = max(y1, y2);		
			} else {
				this.y1 = y1;
				this.y2 = y2;
			}
		}
	}

	public Double[] leftEnd() {
		return new Double[]{x1, y1, 0.0};
	}
	
	public Double[] rightEnd() {
		return new Double[]{x2, y2, 1.0};
	}
	
	private Double direction(Double[] p0, Double[] p1, Double[] p2) {
		return (p2[0] - p0[0]) * (p1[1] - p0[1]) - (p1[0] - p0[0]) * (p2[1] - p0[1]); 
	}
	
	private boolean onSegment(Double[] p0, Double[] p1, Double[] p2) {
		return 	(p2[0] >= min(p0[0], p1[0]) && p2[0] <= max(p0[0], p1[0])) &&
				(p2[1] >= min(p0[1], p1[1]) && p2[1] <= max(p0[1], p1[1]));
	}
	
	
	public boolean isCrossed(Segment another) {
		if (another == null) return false;
		
		Double d1 = direction(another.leftEnd(), another.rightEnd(), leftEnd());
		Double d2 = direction(another.leftEnd(), another.rightEnd(), rightEnd());
		Double d3 = direction(leftEnd(), rightEnd(), another.leftEnd());
		Double d4 = direction(leftEnd(), rightEnd(), another.rightEnd());
		
		if ((d1 * d2 < 0.0) && (d3 * d4 < 0.0)) return true;
		
		if (d1.equals(0.0) && onSegment(another.leftEnd(), another.rightEnd(), leftEnd())) return true;
		else if (d2.equals(0.0) && onSegment(another.leftEnd(), another.rightEnd(), rightEnd())) return true;
		else if (d3.equals(0.0) && onSegment(leftEnd(), rightEnd(), another.leftEnd())) return true;
		else if (d4.equals(0.0) && onSegment(leftEnd(), rightEnd(), another.rightEnd())) return true;
		
		return false;
	}
	
	public Double[] crossingPoint(Segment another) {
		if (!isCrossed(another)) return null;
		Double k1 = (y2 - y1) / (x2 - x1);
		Double d1 = (y1 * x2 - x1 * y2) / (x2 - x1);
		
		Double k2 = (another.y2 - another.y1) / (another.x2 - another.x1);
		Double d2 = (another.y1 * another.x2 - another.x1 * another.y2) / (another.x2 - another.x1);
		
		Double x = (d2 - d1) / (k2 -k1);
		Double y = k1 * x - d1;
		
		if (!x.equals(0.0)) x = -x;
		else x = 0.0;
		if (!y.equals(0.0)) y = -y;
		else y = 0.0;
		
		return new Double[]{x, y};
	}

	@Override
	public int compareTo(Segment o) {
		if (!y1.equals(o.y1)) return y1.compareTo(o.y1); // compare by y of left end, if this.y > o.y that this above o
		else return y2.compareTo(o.y2);
	}

	@Override
	public String toString() {
		return "[" + x1 + ", " + y1 + ", " + x2 + ", " + y2	+ "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((x1 == null) ? 0 : x1.hashCode());
		result = prime * result + ((x2 == null) ? 0 : x2.hashCode());
		result = prime * result + ((y1 == null) ? 0 : y1.hashCode());
		result = prime * result + ((y2 == null) ? 0 : y2.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Segment))
			return false;
		Segment other = (Segment) obj;
		if (x1 == null) {
			if (other.x1 != null)
				return false;
		} else if (!x1.equals(other.x1))
			return false;
		if (x2 == null) {
			if (other.x2 != null)
				return false;
		} else if (!x2.equals(other.x2))
			return false;
		if (y1 == null) {
			if (other.y1 != null)
				return false;
		} else if (!y1.equals(other.y1))
			return false;
		if (y2 == null) {
			if (other.y2 != null)
				return false;
		} else if (!y2.equals(other.y2))
			return false;
		return true;
	}
}
